原本要介紹Type Guard和Narrowing,後來發現Type Predicates是Narrowing的一種技巧,所以今天先來看看Type Predicates吧!
Type predicate 的 predicate 是電腦科學裡的一個用語,指的是「一個會給予TRUE或FALSE的邏輯運算」。
在TypeScript裡,type predicate專門用於函式(function),會在函式回傳 TRUE
或 FALSE
的同時,判斷參數的型別是否為特定型別,語法如範例中的 obj is A
:
function returnA(obj: A | B): obj is A {
return typeof (obj as A).aProp !== "undefined";
}
returnA函式的參數可能是A或B型別,這裡先假設returnA函式參數obj是A型別,如果obj的aProp屬性不是undefined,就會回傳 true
。
而 obj is A
就是讓TypeScript compiler知道,如果returnA函式回傳 true
,那麼同時也會告訴compiler「obj物件是A型別」;若函式回傳 false
,則會跟compiler說「obj物件是B型別」。
如果沒有用type predicate,也就是 ─ 如果沒有寫上 obj is A
,縱使returnA函式回傳 true
,但因為 函式參數可能是A或B型別,所以並不會告知 TypeScript compiler obj 是A還是B型別。
到這裡可能還是會對type predicate的概念有點模糊,所以接著來看一個例子。
範例定義一個可以是Human或Gorilla的Union型別Animal,然後宣告一個「確認傳入參數是不是Human型別」的isHuman函式,以及宣告另一個showNameOrId函式來判斷「若ani參數是Human型別,顯示ani的name屬性;反之,若ani參數是Gorilla型別則秀出ani的gorillaId屬性」:
interface Human {
name: string;
}
interface Gorilla {
gorillaId: number
}
type Animal = Human | Gorilla;
function isHuman(animal: Animal) {
return typeof (animal as Human).name !== "undefined";
}
function showNameOrId(ani: Animal) {
if(isHuman(ani)){
alert(ani.name);
}
else{
alert(ani.gorillaId);
}
}
const xiaMing: Human = {name: 'xia ming'}
showNameOrId(xiaMing); // error
結果執行後出現以下兩則錯誤訊息:
Property 'name' does not exist on type 'Animal'.
Property 'name' does not exist on type 'Gorilla'.(2339)
Property 'gorillaId' does not exist on type 'Animal'.
Property 'gorillaId' does not exist on type 'Human'.(2339)
原因如前面所說,因為isHuman函式的參數宣告成有可能是Human或Gorilla型別。
雖然xiaMing是Human物件,但xiaMing是isHuman函式的引數,即使函式確認xiaMing的name不是undefined,也回傳 true
,compiler仍認為isHuman函式的引數可能是Human型別或Gorilla型別而報錯。
這時可以用到type predicate的技巧,修改一下isHuman函式的回傳值型別:
function isHuman(animal: Animal): animal is Human {
return typeof (animal as Human).name !== "undefined";
}
animal is Human
代表要讓isHuman函式回傳true之後,同時要將animal參數當作是Human型別。
因此,修改過後再次呼叫isHuman函式時,xiaMing才會真正被視為是Human物件,同時把xiaMing是Human物件的資訊傳遞給compiler知道。
這次的概念有一點點點複雜,有興趣的人可以玩一下範例,看看把 /* animal is Human */
這一小段註解拿掉前後的差異。
參考資料
Using type predicates @TypeScript Handbook
What is a predicate?
TypeScript Type Guards and Type Predicates
TypeScript: Type predicates